#include <string.h>
#include <assert.h>
#include <limits.h>
#include <stdlib.h>

#include <cm/cm_hash.h>
#include "cm_atom.h"

static struct atom {
	struct atom *link;
	int len;
	char *str;
}*buckets[2048];

#define NELEMS(x) ((sizeof (x)) / (sizeof((x)[0])))

int atom_length(const char *str)
{
	struct atom *p;
	int i;

	assert(str);
	for (i = 0; i < NELEMS(buckets); i++) {
		for (p = buckets[i]; p; p = p->link)
			if (p->str == str)
				return p->len;
	}
	assert(0);
	return 0;
}

const char * atom_new(const char *str, int len)
{
	unsigned long h;
	int i;
	struct atom *p;

	assert(str);
	assert(len >= 0);
	// calculate hash
	h = ELFHash(str, len);
	h %= NELEMS(buckets);
	for (p = buckets[h]; p; p = p->link) {
		if (len == p->len) {
			for (i = 0; i < len && p->str[i] == str[i];)
				i++;
			if (i == len)
				return p->str;
		}
		p = malloc(sizeof(*p) + len + 1);
		p->len = len;
		p->str = (char *) (p + 1);
		if (len > 0)
			memcpy(p->str, str, len);
		p->str[len] = '\0';
		p->link = buckets[h];
		buckets[h] = p;
	}
	return p->str;
}

const char * atom_string(const char *str)
{
	assert(str);
	return atom_new(str, strlen(str));
}

const char *atom_int(long n)
{
	char str[43];
	char *s = str + sizeof str;
	unsigned long m;

	if (n == LONG_MIN)
		m = LONG_MAX + 1UL;
	else if (n < 0)
		m = -n;
	else
		m = n;

	do
		*--s = m % 10 + '0';
	while ((m /= 10) > 0);

	if (n < 0)
		*--s = '-';
	return atom_new(s, (str + sizeof str) - s);
}
